package com.cl.cloud.binlog.listener;

import cn.hutool.core.collection.CollectionUtil;
import com.cl.cloud.binlog.content.BinLogApplicationContext;
import com.cl.cloud.binlog.model.BinlogPositionEntity;
import com.cl.cloud.binlog.parser.CommonEventParserDispatcher;
import com.cl.cloud.binlog.parser.EventEntity;
import com.cl.cloud.binlog.parser.EventStrategy;
import com.cl.cloud.binlog.properties.DbConfig;
import com.cl.cloud.binlog.tableMeta.TableMetaFactory;
import com.github.shyiko.mysql.binlog.BinaryLogClient;
import com.github.shyiko.mysql.binlog.event.Event;
import com.github.shyiko.mysql.binlog.event.EventHeaderV4;
import com.github.shyiko.mysql.binlog.event.EventType;
import com.github.shyiko.mysql.binlog.event.RotateEventData;
import lombok.AllArgsConstructor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @description:
 * @author: liuzijian
 * @time: 2022-07-08 18:03
 */
public class MultiEventHandlerListener implements BinaryLogClient.EventListener {

    private final BinLogApplicationContext context;

    private final DbConfig dbConfig;

    private final EventStrategy eventStrategy;

    public MultiEventHandlerListener(BinLogApplicationContext context, DbConfig dbConfig) {
        this.context = context;
        this.dbConfig = dbConfig;
        eventStrategy = new CommonEventParserDispatcher(new TableMetaFactory(dbConfig, context));
    }

    /**
     * 不计入position更新的事件类型
     */
    private final static Set<EventType> excludePositionEvent = new HashSet<>();

    static {
        excludePositionEvent.add(EventType.FORMAT_DESCRIPTION);
        excludePositionEvent.add(EventType.HEARTBEAT);
    }

    @Override
    public void onEvent(Event event) {
        //获取当前事件的类型
        EventType eventType = event.getHeader().getEventType();
        if (!excludePositionEvent.contains(eventType)) {
            BinlogPositionEntity binlogPositionEntity = new BinlogPositionEntity();
            BinlogPositionEntity oldBinlogPositionEntity = new BinlogPositionEntity();
            //处理rotate事件,这里会替换调binlog fileName
            if (event.getHeader().getEventType().equals(EventType.ROTATE)) {
                RotateEventData rotateEventData = event.getData();
                binlogPositionEntity.setBinlogName(rotateEventData.getBinlogFilename());
                binlogPositionEntity.setPosition(rotateEventData.getBinlogPosition());
                binlogPositionEntity.setServerId(event.getHeader().getServerId());
            } else { //统一处理事件对应的binlog position
                BinlogPositionEntity redisInfo = context.getPositionHandler().getPosition(dbConfig);
                if(redisInfo != null){
                    oldBinlogPositionEntity = binlogPositionEntity = redisInfo;
                }
                EventHeaderV4 eventHeaderV4 = event.getHeader();
                binlogPositionEntity.setPosition(eventHeaderV4.getPosition());
                binlogPositionEntity.setServerId(event.getHeader().getServerId());
            }
            context.getPositionHandler().savePosition(dbConfig, binlogPositionEntity);
            try{
                List<EventEntity> eventEntityList = eventStrategy.handle(event);
                if(CollectionUtil.isNotEmpty(eventEntityList)){
                    for(EventEntity eventEntity : eventEntityList){
                        switch (eventEntity.getEventEntityType()){
                            case UPDATE:
                                eventEntity.getChangeAfterEntity().updateHandle(eventEntity.getChangeBeforeEntity());
                                break;
                            case INSERT:
                                eventEntity.getChangeAfterEntity().insertHandle();
                                break;
                            case DELETE:
                                eventEntity.getChangeAfterEntity().deleteHandle();
                                break;
                            default:
                        }
                    }
                }
            }catch (Exception e){
                //binLogApplicationContext.getPositionHandler().savePosition(dbConfig, oldBinlogPositionEntity);
                throw e;
            }
        }
    }
}
